import 'package:flutter/widgets.dart';

class ExtendedListView extends StatefulWidget {
  const ExtendedListView({
    required this.itemCount,
    required this.itemBuilder,
    this.headerBuilder,
    this.footerBuilder,
    this.dividerBuilder,
    this.bottomSafeArea = false,
    this.physics,
    this.headerSliverBuilder,
    this.controller,
    this.padding,
    this.shrinkWrap = false,
    super.key,
  });

  final int itemCount;
  final WidgetBuilder? headerBuilder;
  final WidgetBuilder? footerBuilder;
  final IndexedWidgetBuilder itemBuilder;
  final IndexedWidgetBuilder? dividerBuilder;
  final bool bottomSafeArea;
  final ScrollPhysics? physics;
  final ScrollController? controller;
  final NestedScrollViewHeaderSliversBuilder? headerSliverBuilder;
  final EdgeInsetsGeometry? padding;
  final bool shrinkWrap;

  @override
  State<StatefulWidget> createState() => _ExtendedListViewState();
}

enum ItemType {
  header,
  footer,
  data,
  dividerData,
}

class _ExtendedListViewState extends State<ExtendedListView> {
  bool get isNested => widget.headerSliverBuilder != null;

  @override
  Widget build(BuildContext context) {
    if (isNested) {
      return NestedScrollView(
        headerSliverBuilder: widget.headerSliverBuilder!,
        physics: widget.physics,
        controller: widget.controller,
        body: MediaQuery.removePadding(
          context: context,
          removeTop: true,
          child: _buildList(),
        ),
      );
    } else {
      return _buildList();
    }
  }

  Widget _bottomSafeAreaWrapper(Widget child) {
    return SafeArea(
      top: false,
      left: false,
      right: false,
      bottom: true,
      child: child,
    );
  }

  Widget _itemBuilder(BuildContext context, int index) {
    final headerCount = _headerCount();
    final totalItemCount = _dataItemCount() + headerCount + _footerCount();
    switch (_itemType(index, totalItemCount)) {
      case ItemType.header:
        return widget.headerBuilder!(context);
      case ItemType.footer:
        return widget.bottomSafeArea && index == totalItemCount - 1
            ? _bottomSafeAreaWrapper(widget.footerBuilder!(context))
            : widget.footerBuilder!(context);
      case ItemType.dividerData:
        return widget.bottomSafeArea && index == totalItemCount - 1
            ? _bottomSafeAreaWrapper(_buildDividerWithData(context, index - headerCount))
            : _buildDividerWithData(context, index - headerCount);
      case ItemType.data:
        return widget.bottomSafeArea && index == totalItemCount - 1
            ? _bottomSafeAreaWrapper(widget.itemBuilder(context, index - headerCount))
            : widget.itemBuilder(context, index - headerCount);
      default:
        throw Exception();
    }
  }

  Widget _buildList() {
    final headerCount = _headerCount();
    final totalItemCount = _dataItemCount() + headerCount + _footerCount();
    return ListView.builder(
      physics: isNested ? null : widget.physics,
      controller: isNested ? null : widget.controller,
      padding: widget.padding,
      itemCount: totalItemCount,
      itemBuilder: _itemBuilder,
      shrinkWrap: widget.shrinkWrap,
    );
  }

  ItemType _itemType(itemIndex, totalItemCount) {
    if (_isHeader(itemIndex)) {
      return ItemType.header;
    } else if (_isFooter(itemIndex, totalItemCount)) {
      return ItemType.footer;
    } else if (_hasDivider()) {
      return ItemType.dividerData;
    } else {
      return ItemType.data;
    }
  }

  Widget _buildDividerWithData(BuildContext context, int dataIndex) {
    if (dataIndex.isEven) {
      return widget.itemBuilder(context, dataIndex ~/ 2);
    } else {
      return widget.dividerBuilder!(context, dataIndex ~/ 2);
    }
  }

  bool _isHeader(int itemIndex) => _hasHeader() && itemIndex == 0;

  bool _isFooter(int itemIndex, int total) => _hasFooter() && itemIndex == total - 1;

  int _headerCount() => _hasHeader() ? 1 : 0;

  int _footerCount() => _hasFooter() ? 1 : 0;

  int _dataItemCount() {
    if (_hasDivider() && widget.itemCount > 0) {
      return widget.itemCount * 2 - 1;
    }
    return widget.itemCount;
  }

  bool _hasDivider() => widget.dividerBuilder != null;

  bool _hasHeader() => widget.headerBuilder != null;

  bool _hasFooter() => widget.footerBuilder != null;
}
